package com.guiji.file.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.http.HttpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.tobato.fastdfs.domain.fdfs.StorePath;
import com.github.tobato.fastdfs.domain.proto.storage.DownloadByteArray;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import com.guiji.common.core.mybatisplus.core.ServicePlusImpl;
import com.guiji.common.core.page.PagePlus;
import com.guiji.common.core.page.TableDataInfo;
import com.guiji.common.exception.CustomException;
import com.guiji.common.exception.file.FileNameLengthLimitExceededException;
import com.guiji.common.exception.file.FileSizeLimitExceededException;
import com.guiji.common.exception.file.InvalidExtensionException;
import com.guiji.common.utils.PageUtils;
import com.guiji.common.utils.file.FileUploadUtils;
import com.guiji.file.bo.ResourceEditBo;
import com.guiji.file.bo.ResourceQueryBo;
import com.guiji.file.domain.Resource;
import com.guiji.file.mapper.ResourceMapper;
import com.guiji.file.service.IResourceService;
import com.guiji.file.vo.ResourceVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * 文件资源Service业务层处理
 *
 * @author 钟维澎（四月）
 * @date 2021-06-29
 */
@Service
@Slf4j
public class ResourceServiceImpl extends ServicePlusImpl<ResourceMapper, Resource> implements IResourceService {

	@Autowired
	private ResourceMapper resourceMapper;

	/**
	 * 域名或本机访问地址
	 */
	@Value("${fdfs.domain}")
	public String domain;

	@Autowired
	private FastFileStorageClient storageClient;

	@Override
	public ResourceVo queryById(Long id) {
		return getVoById(id, ResourceVo.class);
	}

	@Override
	public TableDataInfo<ResourceVo> queryPageList(ResourceQueryBo bo) {
		PagePlus<Resource, ResourceVo> result = pageVo(PageUtils.buildPagePlus(), buildQueryWrapper(bo), ResourceVo.class);
		return PageUtils.buildDataInfo(result);
	}

	@Override
	public List<ResourceVo> queryList(ResourceQueryBo bo) {
		return listVo(buildQueryWrapper(bo), ResourceVo.class);
	}

	private LambdaQueryWrapper<Resource> buildQueryWrapper(ResourceQueryBo bo) {
		Map<String, Object> params = bo.getParams();
		LambdaQueryWrapper<Resource> lqw = Wrappers.lambdaQuery();
		lqw.eq(StrUtil.isNotBlank(bo.getMd5()), Resource::getMd5, bo.getMd5());
		lqw.like(StrUtil.isNotBlank(bo.getName()), Resource::getName, bo.getName());
		lqw.like(StrUtil.isNotBlank(bo.getPostfix()), Resource::getPostfix, bo.getPostfix());
		lqw.eq(bo.getBucketId() != null, Resource::getBucketId, bo.getBucketId());
		lqw.eq(StrUtil.isNotBlank(bo.getUploadPath()), Resource::getUploadPath, bo.getUploadPath());
		lqw.eq(bo.getVersion() != null, Resource::getVersion, bo.getVersion());
		lqw.eq(bo.getCreateTime() != null, Resource::getCreateTime, bo.getCreateTime());
		lqw.eq(StrUtil.isNotBlank(bo.getCreateBy()), Resource::getCreateBy, bo.getCreateBy());
		lqw.eq(bo.getUpdateTime() != null, Resource::getUpdateTime, bo.getUpdateTime());
		lqw.eq(StrUtil.isNotBlank(bo.getUpdateBy()), Resource::getUpdateBy, bo.getUpdateBy());
		return lqw;
	}

	@Override
	public Boolean insertByAddBo(MultipartFile file) {
		String uploadPath = "", md5 = "";

		try {
			md5 = SecureUtil.md5(file.getInputStream());
		} catch (IOException e) {
			log.error("获取文件流失败", e);
			throw new CustomException("获取文件流失败" + e.getMessage());
		}
		ResourceQueryBo resourceQueryBo = new ResourceQueryBo();
		resourceQueryBo.setMd5(md5);
		if (count(buildQueryWrapper(resourceQueryBo)) > 0) {
			return false;
		}

		try {
			uploadPath = uploadFile(file);
		} catch (FileSizeLimitExceededException fileSizeLimitExceededException) {
			log.error("文件最大不能超过50M", fileSizeLimitExceededException);
		} catch (FileNameLengthLimitExceededException fileNameLengthLimitExceededException) {
			log.error("文件名不能超过100个字", fileNameLengthLimitExceededException);
		} catch (IOException ioException) {
			log.error("读写文件出错", ioException);
		} catch (InvalidExtensionException invalidExtensionException) {
			log.error("文件校验异常", invalidExtensionException);
		}

		Resource add = new Resource();
		add.setUploadPath(uploadPath);
		add.setMd5(md5);
		add.setName(file.getOriginalFilename());
		add.setPostfix(FileUtil.extName(file.getOriginalFilename()));
		return save(add);
	}

	/**
	 * FastDfs文件上传
	 *
	 * @param file 上传的文件
	 * @return 返回上传成功的存储文件的路径信息
	 * @throws FileSizeLimitExceededException       如果超出最大大小
	 * @throws FileNameLengthLimitExceededException 文件名太长
	 * @throws IOException                          比如读写文件出错时
	 * @throws InvalidExtensionException            文件校验异常
	 */
	private String uploadFile(MultipartFile file) throws FileSizeLimitExceededException,
		IOException, FileNameLengthLimitExceededException, InvalidExtensionException {
		StorePath storePath = FileUploadUtils.upload(storageClient, file, null);
		return domain + "/" + storePath.getFullPath();
	}

	@Override
	public Boolean updateByEditBo(ResourceEditBo bo) {
		Resource update = BeanUtil.toBean(bo, Resource.class);
		validEntityBeforeSave(update);
		return updateById(update);
	}

	/**
	 * 保存前的数据校验
	 *
	 * @param entity 实体类数据
	 */
	private void validEntityBeforeSave(Resource entity) {
		//TODO 做一些数据校验,如唯一约束
		String name = entity.getName();
		if(!name.contains(".")){
			throw new CustomException("名称没有后缀名");
		}
		String[] split = name.split("\\.");
		String suffix = entity.getPostfix();
		if(split[1].equals(suffix)){
			return;
		}
		throw new CustomException("后缀名不一致");
	}

	@Override
	public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
		if (isValid) {
			// 做一些业务上的校验,判断是否需要校验
			List<Resource> list = listByIds(ids);
			try {
				for (Resource resource : list) {
					storageClient.deleteFile(resource.getUploadPath());
				}
			} catch (Exception e) {
				return false;
			}
		}
		int result = resourceMapper.deleteBatchIds(ids);
		return result > 0 ? true : false;
	}

	@Override
	public void download(HttpServletResponse response, Long id) {
		ResourceVo resource = null;
		try {
			resource = queryById(id);
		} catch (Exception e) {
			log.error("查询数据库发生异常" + e.getMessage(),e);
			throw new CustomException("数据库发生异常" + e.getMessage());
		}

		response.setContentType("multipart/form-data");
		response.setHeader("Content-Disposition", "attachment;filename=" + resource.getName());
		ServletOutputStream outputStream = null;
		try {
			outputStream = response.getOutputStream();
		} catch (IOException e) {
			log.error("获取响应的输出流发生异常", e);
			throw new CustomException("系统异常!");
		}
		String fileUrl = resource.getUploadPath().replace(domain + "/","");
		String group = fileUrl.substring(0, fileUrl.indexOf("/"));
		String path = fileUrl.substring(fileUrl.indexOf("/") + 1);
		DownloadByteArray downloadByteArray = new DownloadByteArray();
		byte[] bytes = storageClient.downloadFile(group, path, downloadByteArray);
		IoUtil.write(outputStream,true,bytes);
	}
}
